/**
 * 存储服务模块
 * 处理数据的持久化存储和缓存
 */

export class StorageService {
  /** 单例实例 */
  private static instance: StorageService;
  /** 内存缓存 */
  private cache = new Map<string, any>();
  /** 缓存过期时间映射 */
  private cacheExpiry = new Map<string, number>();

  private constructor() {}

  static getInstance(): StorageService {
    if (!StorageService.instance) {
      StorageService.instance = new StorageService();
    }
    return StorageService.instance;
  }

  /**
   * 保存数据到本地存储
   */
  async save(key: string, data: any, options?: { ttlMs?: number }): Promise<void> {
    try {
      const expiresAt = options?.ttlMs && options.ttlMs > 0 ? Date.now() + options.ttlMs : undefined;
      const storageData = {
        value: data,
        timestamp: Date.now(),
        version: "1.0",
        ...(expiresAt ? { expiresAt } : {}),
      };

      const existing = await browser.storage.local.get(key);
      const prev = existing?.[key];
      const prevStr = prev ? JSON.stringify(prev) : "";
      const nextStr = JSON.stringify(storageData);
      if (prevStr === nextStr) return
      await browser.storage.local.set({ [key]: storageData });

      // 更新缓存
      this.cache.set(key, storageData);
      if (expiresAt) {
        this.cacheExpiry.set(key, expiresAt);
      } else {
        this.cacheExpiry.delete(key); // 不设置过期时间则永久不过期
      }

      console.log(`Data saved to storage: ${key}`);
    } catch (error) {
      console.error(`Failed to save data for key ${key}:`, error);
      throw error;
    }
  }

  /**
   * 从本地存储获取数据
   */
  async get(key: string): Promise<any> {
    try {
      // 检查缓存
      if (this.cache.has(key) && this.isCacheValid(key)) {
        return this.cache.get(key)?.value;
      }

      const result = await browser.storage.local.get(key);
      const storageData = result[key];

      if (storageData) {
        // 若设置了过期时间且已过期，则删除并返回null
        if (typeof storageData.expiresAt === "number" && Date.now() >= storageData.expiresAt) {
          await this.delete(key);
          return null;
        }
        // 更新缓存
        this.cache.set(key, storageData);
        if (typeof storageData.expiresAt === "number") {
          this.cacheExpiry.set(key, storageData.expiresAt);
        } else {
          this.cacheExpiry.delete(key); // 未设置过期时间则永久不过期
        }

        return storageData.value;
      }

      return null;
    } catch (error) {
      console.error(`Failed to get data for key ${key}:`, error);
      throw error;
    }
  }

  /**
   * 获取所有存储的数据
   */
  async getAll(): Promise<Record<string, any>> {
    try {
      const result = await browser.storage.local.get(null);
      const allData: Record<string, any> = {};

      for (const [key, storageData] of Object.entries(result)) {
        if (
          storageData &&
          typeof storageData === "object" &&
          (storageData as any).value !== undefined &&
          (!("expiresAt" in (storageData as any)) ||
            typeof (storageData as any).expiresAt !== "number" ||
            Date.now() < (storageData as any).expiresAt)
        ) {
          allData[key] = storageData.value;
        }
      }

      return allData;
    } catch (error) {
      console.error("Failed to get all data:", error);
      throw error;
    }
  }

  /**
   * 删除指定键的数据
   */
  async delete(key: string): Promise<void> {
    try {
      await browser.storage.local.remove(key);

      // 清除缓存
      this.cache.delete(key);
      this.cacheExpiry.delete(key);

      console.log(`Data deleted from storage: ${key}`);
    } catch (error) {
      console.error(`Failed to delete data for key ${key}:`, error);
      throw error;
    }
  }

  /**
   * 清空所有存储数据
   */
  async clear(): Promise<void> {
    try {
      await browser.storage.local.clear();

      // 清空缓存
      this.cache.clear();
      this.cacheExpiry.clear();

      console.log("All storage data cleared");
    } catch (error) {
      console.error("Failed to clear storage:", error);
      throw error;
    }
  }

  /**
   * 获取存储使用统计
   */
  async getStorageStats(): Promise<{
    bytesInUse: number;
    totalItems: number;
    keys: string[];
  }> {
    try {
      const allData = await browser.storage.local.get(null);
      const keys = Object.keys(allData);

      // 计算大致的字节使用量
      const bytesInUse = JSON.stringify(allData).length;

      return {
        bytesInUse,
        totalItems: keys.length,
        keys,
      };
    } catch (error) {
      console.error("Failed to get storage stats:", error);
      throw error;
    }
  }

  /**
   * 批量保存数据
   */
  async saveBatch(data: Record<string, any>, options?: { ttlMs?: number }): Promise<void> {
    try {
      const storageData: Record<string, any> = {};
      const timestamp = Date.now();
      const expiresAt = options?.ttlMs && options.ttlMs > 0 ? timestamp + options.ttlMs : undefined;

      for (const [key, value] of Object.entries(data)) {
        storageData[key] = {
          value,
          timestamp,
          version: "1.0",
          ...(expiresAt ? { expiresAt } : {}),
        };

        // 更新缓存
        this.cache.set(key, storageData[key]);
        if (expiresAt) {
          this.cacheExpiry.set(key, expiresAt);
        } else {
          this.cacheExpiry.delete(key);
        }
      }

      await browser.storage.local.set(storageData);
      console.log(`Batch saved ${Object.keys(data).length} items`);
    } catch (error) {
      console.error("Failed to save batch data:", error);
      throw error;
    }
  }

  /**
   * 检查缓存是否有效
   */
  private isCacheValid(key: string): boolean {
    const expiry = this.cacheExpiry.get(key);
    if (expiry === undefined) {
      // 未设置过期时间则认为永久有效
      return true;
    }
    return Date.now() < expiry;
  }

  /**
   * 清理过期缓存
   */
  clearExpiredCache(): void {
    const now = Date.now();
    for (const [key, expiry] of this.cacheExpiry.entries()) {
      if (now >= expiry) {
        this.cache.delete(key);
        this.cacheExpiry.delete(key);
      }
    }
  }

  /**
   * 监听存储变化
   */
  onStorageChanged(callback: (changes: Record<string, Browser.storage.StorageChange>) => void): void {
    browser.storage.local.onChanged.addListener(callback);
  }
}
